package com.gome.gop.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.gome.gop.data.entity.source.PageAnalyseLineRet;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import com.gome.gop.common.Page;
import com.gome.gop.common.UUIDUtil;
import com.gome.gop.data.entity.source.AddSourceParam;
import com.gome.gop.data.entity.source.AnalyseColumnDo;
import com.gome.gop.data.entity.source.AnalyseDo;
import com.gome.gop.data.entity.source.AnalyseLineDo;
import com.gome.gop.data.entity.source.PageAnalyseParam;
import com.gome.gop.data.entity.utils.PageUtils;
import com.gome.gop.data.mapper.AnalyseColumnDoMapper;
import com.gome.gop.data.mapper.AnalyseDoMapper;
import com.gome.gop.data.mapper.AnalyseLineDoMapper;
import com.gome.gop.service.SourceService;
import com.gome.gop.util.export.ExportFile;
import com.gome.gop.util.export.ExportSheet;

/**
 * @Title:
 * @Description:
 * @Author:chengcheng7
 * @Since: 2022/10/1 15:28
 * @Copyright:Copyright (c) 2018 - 2100
 * @Company:gome
 * @Version:1.1.0
 */
@Service
public class SourceServiceImpl implements SourceService {
    
    private static final String start = "start210a7a385f5b4ec9";
    
    private static final String end = "end210a7a385f5b4ec9";
    
    @Resource
    private AnalyseDoMapper analyseDoMapper;
    
    @Resource
    private AnalyseLineDoMapper analyseLineDoMapper;
    
    @Resource
    private AnalyseColumnDoMapper analyseColumnDoMapper;
    
    @Override
    public PageAnalyseLineRet addAnalyse(AddSourceParam addSourceParam) {
        
        return rowToPageAnalyseLineRet(obtainRows(addSourceParam));
        
    }
    
    private Page rowsToPage(List rows) {
        
        return new Page(0, rows.size(), rows);
        
    }
    
    @Override
    public PageAnalyseLineRet saveAnalyse(AddSourceParam addSourceParam) {
        
        AnalyseDo analyseDo = obtainAnalyseDo(addSourceParam);
        List<AnalyseLineDo> analyseLineDos = obtainAnalyseLineDoList(addSourceParam, analyseDo);
        List<List<AnalyseColumnDo>> analyseColumnDo2List = obtainAnalyseColumnDoList(analyseLineDos, addSourceParam);
        saveAnalyseDo(analyseDo);
        saveAnalyseLineDos(analyseLineDos, analyseDo);
        saveAnalyseColumnDo2List(analyseColumnDo2List, analyseLineDos);
        return rowToPageAnalyseLineRet(queryRows(addSourceParam));
        
    }
    
    @Override
    public Page pageAnalyse(PageAnalyseParam param) {
        
        return PageUtils.page(analyseDoMapper, param);
        
    }
    
    @Override
    public AnalyseDo findAnalyse(AnalyseDo analyseDo) {
        
        return analyseDoMapper.selectByObject(analyseDo);
        
    }
    
    @Override
    public PageAnalyseLineRet pageAnalyseLine(AddSourceParam addSourceParam) {
        
        return rowToPageAnalyseLineRet(queryRows(addSourceParam));
    }
    
    private PageAnalyseLineRet rowToPageAnalyseLineRet(List rows) {
        
        PageAnalyseLineRet pageAnalyseLineRet = new PageAnalyseLineRet();
        pageAnalyseLineRet.setRows(rows);
        pageAnalyseLineRet.setFrozenTitle(getFrozenTitle());
        pageAnalyseLineRet.setTitle(getTitle(pageAnalyseLineRet.getRows()));
        return pageAnalyseLineRet;
        
    }
    
    private List getTitle(List rows) {
        
        List list = new ArrayList();
        if (CollectionUtils.isEmpty(rows)) {
            return list;
        }
        List<HashMap<String, String>> hashMapList = rows;
        HashMap<String, String> maxLengthMap = new HashMap<>();
        for (HashMap<String, String> stringStringHashMap : hashMapList) {
            if (stringStringHashMap == null) {
                continue;
            }
            if (maxLengthMap.size() < stringStringHashMap.size()) {
                maxLengthMap = stringStringHashMap;
            }
        }
        for (int i = 1; i < maxLengthMap.size(); i++) {
            String titleStr = obtainTitleStr(i);
            if (maxLengthMap.get(titleStr) == null) {
                return list;
            }
            list.add(titleStr);
            list.add(obtainAnnotationStr(i));
        }
        return list;
    }
    
    private List getFrozenTitle() {
        List list = new ArrayList();
        list.add(obtainTitleStr(0));
        list.add(obtainAnnotationStr(0));
        return list;
        
    }
    
    @Override
    public void exportAnalyse(PageAnalyseParam param, HttpServletRequest request, HttpServletResponse response) {
        
        List<AnalyseDo> analyseDoList = PageUtils.query(analyseDoMapper, param);
        ExportFile exportFile = new ExportFile();
        exportFile.setRequest(request);
        exportFile.setResponse(response);
        exportFile.setName("代码解析");
        for (AnalyseDo analyseDo : analyseDoList) {
            ExportSheet exportSheet = new ExportSheet();
            exportSheet.setName(analyseDo.getName());
            addSheet(exportSheet, analyseDo);
            exportFile.getExportSheetList().add(exportSheet);
        }
        // exportFile.build();
        exportFile.buildXmind();
    }
    
    @Override
    public void deleteAnalyse(AnalyseDo analyseDo) {
        
        analyseDoMapper.deleteByPrimaryKey(analyseDo.getId());
        
    }
    
    @Override
    public void deleteAnalyseLineDoList(List<AnalyseLineDo> analyseLineDoList) {
        for (AnalyseLineDo analyseLineDo : analyseLineDoList) {
            if (analyseLineDo == null || StringUtils.isBlank(analyseLineDo.getId())) {
                continue;
            }
            analyseLineDoMapper.deleteByPrimaryKey(analyseLineDo.getId());
        }
    }
    
    @Override
    public void exportAnalyseOne(AddSourceParam addSourceParam, HttpServletRequest request, HttpServletResponse response) {
        List<HashMap<String, String>> hashMapList = addSourceParam.getRows();
        ExportFile exportFile = new ExportFile();
        exportFile.setRequest(request);
        exportFile.setResponse(response);
        exportFile.setName("代码解析.xls");
        ExportSheet exportSheet = new ExportSheet();
        exportSheet.setName(addSourceParam.getName());
        for (HashMap<String, String> row : hashMapList) {
            exportSheet.getExportLines().add(obtainColumns(row));
        }
        exportFile.getExportSheetList().add(exportSheet);
        // exportFile.build();
        exportFile.buildXmind();
        
    }
    
    private void addSheet(ExportSheet exportSheet, AnalyseDo analyseDo) {
        AddSourceParam addSourceParam = new AddSourceParam();
        addSourceParam.setOldId(analyseDo.getId());
        List<HashMap<String, String>> rows = queryRows(addSourceParam);
        for (HashMap<String, String> row : rows) {
            exportSheet.getExportLines().add(obtainColumns(row));
        }
    }
    
    private List<String> obtainColumns(HashMap<String, String> row) {
        
        List<String> list = new ArrayList();
        if (row == null || row.isEmpty()) {
            return list;
        }
        for (int i = 0; i < row.size(); i++) {
            if (StringUtils.isBlank(row.get(obtainTitleStr(i)))) {
                break;
            }
            list.add(row.get(obtainTitleStr(i)));
            list.add(row.get(obtainAnnotationStr(i)));
        }
        return list;
        
    }
    
    private List queryRows(AddSourceParam addSourceParam) {
        
        List<HashMap<String, String>> hashMapList = new ArrayList<>();
        List<AnalyseLineDo> list = queryAnalyseLineDo(addSourceParam);
        if (CollectionUtils.isEmpty(list)) {
            return hashMapList;
        }
        for (AnalyseLineDo analyseLineDo : list) {
            HashMap<String, String> stringStringHashMap = new HashMap<>();
            List<AnalyseColumnDo> analyseColumnDoList = queryAnalyseColumnDo(analyseLineDo);
            putColumn(stringStringHashMap, analyseLineDo, analyseColumnDoList);
            hashMapList.add(stringStringHashMap);
        }
        sortRows(hashMapList);
        return hashMapList;
        
    }
    
    private void putColumn(HashMap<String, String> stringStringHashMap, AnalyseLineDo analyseLineDo, List<AnalyseColumnDo> analyseColumnDoList) {
        
        stringStringHashMap.put("id", analyseLineDo.getId());
        for (AnalyseColumnDo analyseColumnDo : analyseColumnDoList) {
            stringStringHashMap.put(analyseColumnDo.getKey(), analyseColumnDo.getValue());
        }
        
    }
    
    private List<AnalyseColumnDo> queryAnalyseColumnDo(AnalyseLineDo analyseLineDo) {
        
        AnalyseColumnDo param = new AnalyseColumnDo();
        param.setLineId(analyseLineDo.getId());
        if (StringUtils.isBlank(param.getLineId())) {
            return null;
        }
        return analyseColumnDoMapper.queryByObject(param);
        
    }
    
    private List<AnalyseLineDo> queryAnalyseLineDo(AddSourceParam addSourceParam) {
        
        AnalyseLineDo analyseLineDo = new AnalyseLineDo();
        analyseLineDo.setAnalyseId(addSourceParam.getOldId());
        if (StringUtils.isBlank(analyseLineDo.getAnalyseId())) {
            return null;
        }
        return analyseLineDoMapper.queryByObject(analyseLineDo);
        
    }
    
    private void saveAnalyseColumnDo2List(List<List<AnalyseColumnDo>> analyseColumnDo2List, List<AnalyseLineDo> analyseLineDos) {
        
        if (CollectionUtils.isEmpty(analyseColumnDo2List)) {
            return;
        }
        for (int i = 0; i < analyseColumnDo2List.size(); i++) {
            saveAnalyseColumnDo1List(analyseColumnDo2List.get(i), analyseLineDos.get(i));
        }
        
    }
    
    private void saveAnalyseColumnDo1List(List<AnalyseColumnDo> analyseColumnDos, AnalyseLineDo analyseLineDo) {
        
        if (CollectionUtils.isEmpty(analyseColumnDos)) {
            return;
        }
        if (StringUtils.isNotBlank(analyseColumnDos.get(0).getLineId())) {
            AnalyseColumnDo param = new AnalyseColumnDo();
            param.setLineId(analyseColumnDos.get(0).getLineId());
            List<AnalyseColumnDo> analyseColumnDosRet = analyseColumnDoMapper.queryByObject(param);
            for (AnalyseColumnDo analyseColumnDo : analyseColumnDosRet) {
                analyseColumnDoMapper.deleteByPrimaryKey(analyseColumnDo.getId());
            }
        }
        for (AnalyseColumnDo analyseColumnDo : analyseColumnDos) {
            analyseColumnDo.setLineId(analyseLineDo.getId());
            analyseColumnDo.setId(UUIDUtil.getUUIDForId());
            analyseColumnDoMapper.insertSelective(analyseColumnDo);
        }
    }
    
    private void saveAnalyseLineDos(List<AnalyseLineDo> analyseLineDos, AnalyseDo analyseDo) {
        for (AnalyseLineDo analyseLineDo : analyseLineDos) {
            saveAnalyseLineDo(analyseLineDo, analyseDo);
        }
    }
    
    private void saveAnalyseLineDo(AnalyseLineDo analyseLineDo, AnalyseDo analyseDo) {
        
        if (StringUtils.isBlank(analyseLineDo.getAnalyseId())) {
            analyseLineDo.setAnalyseId(analyseDo.getId());
        }
        if (StringUtils.isBlank(analyseLineDo.getId())) {
            analyseLineDo.setId(UUIDUtil.getUUIDForId());
            analyseLineDo.setCreateTime(new Date());
            analyseLineDoMapper.insert(analyseLineDo);
            return;
        }
        if (!StringUtils.isNotBlank(analyseLineDo.getId())) {
            analyseLineDo.setUpdateTime(new Date());
            analyseLineDoMapper.updateByPrimaryKeySelective(analyseLineDo);
            return;
        }
        
    }
    
    private List<List<AnalyseColumnDo>> obtainAnalyseColumnDoList(List<AnalyseLineDo> analyseLineDos, AddSourceParam addSourceParam) {
        
        List<List<AnalyseColumnDo>> analyseColumnDo2List = new ArrayList<>();
        
        if (CollectionUtils.isEmpty(analyseLineDos)) {
            return analyseColumnDo2List;
        }
        for (int i = 0; i < addSourceParam.getRows().size(); i++) {
            List<AnalyseColumnDo> analyseColumnDoList = new ArrayList<>();
            for (Map.Entry<String, String> stringStringEntry : addSourceParam.getRows().get(i).entrySet()) {
                AnalyseColumnDo analyseColumnDo = new AnalyseColumnDo();
                analyseColumnDo.setLineId(analyseLineDos.get(i).getId());
                analyseColumnDo.setKey(stringStringEntry.getKey());
                analyseColumnDo.setValue(stringStringEntry.getValue());
                analyseColumnDoList.add(analyseColumnDo);
            }
            analyseColumnDo2List.add(analyseColumnDoList);
        }
        return analyseColumnDo2List;
        
    }
    
    private List<AnalyseLineDo> obtainAnalyseLineDoList(AddSourceParam addSourceParam, AnalyseDo analyseDo) {
        
        List<AnalyseLineDo> analyseLineDoList = new ArrayList<>();
        if (CollectionUtils.isEmpty(addSourceParam.getRows())) {
            return analyseLineDoList;
        }
        for (HashMap<String, String> row : addSourceParam.getRows()) {
            AnalyseLineDo analyseLineDo = new AnalyseLineDo();
            analyseLineDo.setId(row.get("id"));
            analyseLineDoList.add(analyseLineDo);
        }
        return analyseLineDoList;
        
    }
    
    private void saveAnalyseDo(AnalyseDo analyseDo) {
        if (StringUtils.isBlank(analyseDo.getId())) {
            analyseDo.setId(UUIDUtil.getUUIDForId());
            analyseDo.setCreateTime(new Date());
            analyseDoMapper.insert(analyseDo);
        }
        else {
            analyseDo.setUpdateTime(new Date());
            analyseDoMapper.updateByPrimaryKeySelective(analyseDo);
        }
        
    }
    
    private AnalyseDo obtainAnalyseDo(AddSourceParam addSourceParam) {
        
        AnalyseDo analyseDo = new AnalyseDo();
        analyseDo.setName(addSourceParam.getName());
        analyseDo.setId(addSourceParam.getOldId());
        return analyseDo;
        
    }
    
    private List obtainRows(AddSourceParam addSourceParam) {
        
        List<HashMap<String, String>> rows = addSourceParam.getRows();
        if (CollectionUtils.isEmpty(rows)) {
            rows = new ArrayList();
        }
        addRow(rows, addSourceParam.getCodeString());
        sortRows(rows);
        addAnnotation(rows);
        return rows;
        
    }
    
    private void addAnnotation(List<HashMap<String, String>> rows) {
        
        if (CollectionUtils.isEmpty(rows)) {
            return;
        }
        for (HashMap<String, String> row : rows) {
            addAnnotation(row);
        }
    }
    
    private void addAnnotation(HashMap<String, String> row) {
        Integer size = row.size();
        for (Integer i = 0; i < size; i++) {
            String key = obtainAnnotationStr(i);
            if (StringUtils.isBlank(row.get(key))) {
                row.put(key, "");
            }
        }
    }
    
    private void sortRows(List<HashMap<String, String>> rows) {
        Comparator comparator = new Comparator<HashMap<String, String>>() {
            @Override
            public int compare(HashMap<String, String> o1, HashMap<String, String> o2) {
                for (int i = 1; i < o1.size() && i < o1.size(); i++) {
                    String key = obtainTitleStr(i);
                    if (StringUtils.equals(o1.get(key), o2.get(key))) {
                        continue;
                    }
                    if (StringUtils.isBlank(o1.get(key)) && StringUtils.isBlank(o2.get(key))) {
                        continue;
                    }
                    if (StringUtils.isBlank(o1.get(key))) {
                        return 1;
                    }
                    if (StringUtils.isBlank(o2.get(key))) {
                        return -1;
                    }
                    return o1.get(key).compareTo(o2.get(key));
                }
                return 0;
            }
        };
        Collections.sort(rows, comparator);
        
    }
    
    private void addRow(List<HashMap<String, String>> rows, String codeString) {
        if (StringUtils.isBlank(codeString)) {
            return;
        }
        List<HashMap<String, String>> rowsNew = obtainRows(codeString);
        for (HashMap map : rowsNew) {
            addUniqueRow(rows, map);
        }
    }
    
    private void addUniqueRow(List<HashMap<String, String>> rows, HashMap<String, String> map) {
        
        for (int i = 0; i < rows.size(); i++) {
            HashMap row = rows.get(i);
            if (equals(row, map)) {
                String title0 = map.get(obtainTitleStr(0));
                map.putAll(row);
                map.put(obtainTitleStr(0), title0);
                rows.set(i, map);
                return;
            }
        }
        rows.add(map);
        
    }
    
    private boolean equals(HashMap<String, String> row, HashMap<String, String> map) {
        
        for (int i = 0; i < row.size() && i < map.size(); i++) {
            String key = obtainTitleStr(i);
            if (!StringUtils.equals(row.get(key), map.get(key))) {
                return false;
            }
        }
        return true;
        
    }
    
    private List<HashMap<String, String>> obtainRows(String codeString) {
        
        List<HashMap<String, String>> listRet = new ArrayList<>();
        if (StringUtils.isBlank(codeString)) {
            return listRet;
        }
        if (codeString.contains(start) && codeString.contains(end)) {
            codeString = codeString.substring(codeString.indexOf(start) + start.length(), codeString.indexOf(end));
        }
        
        String[] blockList = codeString.split("Breakpoint reached");
        for (String block : blockList) {
            block = block.trim();
            if (!StringUtils.startsWith(block, "at")) {
                continue;
            }
            HashMap<String, String> map = obtainBlockMap(block);
            if (map != null && map.size() > 0) {
                listRet.add(map);
            }
        }
        return listRet;
        
    }
    
    private HashMap<String, String> obtainBlockMap(String block) {
        
        if (StringUtils.isBlank(block)) {
            return null;
        }
        String[] lineList = block.split("\n");
        Integer usefulLength = obtainUsefulLength(lineList);
        String[] lineListTarget = new String[usefulLength + 1];
        lineListTarget[0] = lineList[lineList.length - 1];
        for (int i = 0; i < usefulLength; i++) {
            String line = lineList[i].trim();
            if (StringUtils.isBlank(line)) {
                continue;
            }
            line = line.trim();
            if (!StringUtils.startsWith(line, "at")) {
                lineListTarget[i + 1] = line;
            }
            if (StringUtils.startsWith(line, "at")) {
                String[] lineBlock = line.split("\\(");
                String[] lineBlockPrevious = lineBlock[0].split("\\.");
                String lineRet = lineBlockPrevious[lineBlockPrevious.length - 2] + "." + lineBlockPrevious[lineBlockPrevious.length - 1];
                String[] lineBlockCurrent = lineBlock[1].split(":");
                lineRet = lineRet + ":" + lineBlockCurrent[lineBlockCurrent.length - 1];
                lineListTarget[i + 1] = lineRet;
            }
        }
        CollectionUtils.reverseArray(lineListTarget);
        return listToMap(lineListTarget);
        
    }
    
    private Integer obtainUsefulLength(String[] lineList) {
        
        if (lineList == null || lineList.length == 0) {
            return 0;
        }
        for (int i = 0; i < lineList.length; i++) {
            if (!StringUtils.startsWith(lineList[i].trim(), "at")) {
                return i + 1;
            }
        }
        return 0;
        
    }
    
    private HashMap listToMap(String[] lineStringList) {
        
        HashMap<String, String> map = new HashMap();
        for (int i = 0; i < lineStringList.length; i++) {
            map.put(obtainTitleStr(i), lineStringList[i]);
        }
        return map;
        
    }
    
    private String obtainTitleStr(int i) {
        return "title_" + String.valueOf(i);
    }
    
    private String obtainAnnotationStr(int i) {
        return "annotation_" + String.valueOf(i);
    }
}
